﻿using System;
using System.Collections;
using System.Configuration;
using System.Xml;

namespace System.Web.Core
{
    public class Configuration
    {
        #region Private Members

        private string userDefaultAvatarPath;
        private string defaultLanguage;
        private string orMappingFile;
        private string urlsFile;
        private int userOnlineTimeWindow = 20;
        private string[] registeredDefaultRoles;
        private string[] anonymousDefaultRoles;
        private EntityList registeredDefaultRoleList;
        private EntityList anonymousDefaultRoleList;
        private string ownerRoleName = null;
        private int administratorUserId = 10000;
        private Hashtable providers;
        private Hashtable entityRequests;
        private Hashtable commands;
        private Hashtable tableMappings;
        private Hashtable sites;
        private Hashtable jobs;
        private Type userType;

        private static Configuration instance;

        #endregion

        #region Singleton

        private Configuration()
        {
            ownerRoleName = "所有者";
            Initialize(ConfigurationManager.GetSection("coreConfig") as XmlNode);
        }
        public static Configuration Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new Configuration();
                }
                return instance;
            }
        }

        #endregion

        #region Public Properties

        public string UserDefaultAvatarPath
        {
            get
            {
                return userDefaultAvatarPath;
            }
        }
        public string DefaultLanguage
        {
            get
            {
                return defaultLanguage;
            }
        }
        public string UrlsFile
        {
            get
            {
                return urlsFile;
            }
        }
        public string ORMappingFile
        {
            get
            {
                return orMappingFile;
            }
        }
        public int UserOnlineTimeWindow
        {
            get
            {
                return userOnlineTimeWindow;
            }
        }
        public string[] RegisteredDefaultRoles
        {
            get
            {
                return registeredDefaultRoles;
            }
        }
        public string[] AnonymousDefaultRoles
        {
            get
            {
                return anonymousDefaultRoles;
            }
        }
        public EntityList RegisteredDefaultRoleList
        {
            get
            {
                if (registeredDefaultRoleList == null)
                {
                    Role role;
                    registeredDefaultRoleList = new EntityList();
                    foreach (string roleName in RegisteredDefaultRoles)
                    {
                        role = RoleManager.GetRole(roleName);
                        if (role != null)
                        {
                            registeredDefaultRoleList.Add(role);
                        }
                    }
                }
                return registeredDefaultRoleList;
            }
        }
        public EntityList AnonymousDefaultRoleList
        {
            get
            {
                if (anonymousDefaultRoleList == null)
                {
                    Role role;
                    anonymousDefaultRoleList = new EntityList();
                    foreach (string roleName in AnonymousDefaultRoles)
                    {
                        role = RoleManager.GetRole(roleName);
                        if (role != null)
                        {
                            anonymousDefaultRoleList.Add(role);
                        }
                    }
                }
                return anonymousDefaultRoleList;
            }
        }
        public int AdministratorUserId
        {
            get
            {
                return administratorUserId;
            }
        }
        public string OwnerRoleName
        {
            get
            {
                return ownerRoleName;
            }
        }
        public Hashtable Providers
        {
            get
            {
                return providers;
            }
        }
        public Hashtable Jobs
        {
            get
            {
                return jobs;
            }
        }
        public Hashtable Sites
        {
            get
            {
                return sites;
            }
        }
        public Hashtable TableMappings
        {
            get
            {
                return tableMappings;
            }
        }
        public Hashtable EntityRequests
        {
            get
            {
                return entityRequests;
            }
        }
        public Type UserType
        {
            get
            {
                return userType;
            }
        }

        #endregion

        #region Public Methods

        public bool IsOwnerRole(string roleName)
        {
            if (string.IsNullOrEmpty(roleName))
            {
                return false;
            }
            return roleName == OwnerRoleName;
        }

        public Command GetCommand(string commandIdent)
        {
            return commands[commandIdent] as Command;
        }
        public Command GetCreateCommand(Type entityType)
        {
            return GetCommand(((TableMapping)tableMappings[entityType]).CreateCommandIdent);
        }
        public Command GetRetrieveCommand(Type entityType)
        {
            return GetCommand(((TableMapping)tableMappings[entityType]).RetrieveCommandIdent);
        }
        public Command GetUpdateCommand(Type entityType)
        {
            return GetCommand(((TableMapping)tableMappings[entityType]).UpdateCommandIdent);
        }
        public Command GetDeleteCommand(Type entityType)
        {
            return GetCommand(((TableMapping)tableMappings[entityType]).DeleteCommandIdent);
        }

        public string GetDBFieldName(Type entityType, string propertyName)
        {
            foreach (TableMapping mapping in tableMappings.Values)
            {
                if (entityType.IsAssignableFrom(mapping.EntityType))
                {
                    FieldMapping fieldMapping = mapping.FieldMappings[propertyName] as FieldMapping;
                    if (fieldMapping != null)
                    {
                        return fieldMapping.FieldName;
                    }
                }
            }
            return null;
        }
        public FieldMapping GetFieldMapping(Type entityType, string propertyName)
        {
            foreach (TableMapping mapping in tableMappings.Values)
            {
                if (entityType.IsAssignableFrom(mapping.EntityType))
                {
                    return mapping.FieldMappings[propertyName] as FieldMapping;
                }
            }
            return null;
        }
        public FieldMapping GetFieldMappingByFieldName(Type entityType, string fieldName)
        {
            foreach (TableMapping mapping in tableMappings.Values)
            {
                if (entityType.IsAssignableFrom(mapping.EntityType))
                {
                    foreach (FieldMapping fieldMapping in mapping.FieldMappings.Values)
                    {
                        if (fieldMapping.FieldName == fieldName)
                        {
                            return fieldMapping;
                        }
                    }
                }
            }
            return null;
        }
        public string GetPropertyName(Type entityType, string fieldName)
        {
            TableMapping tableMapping = null;

            foreach (TableMapping mapping in tableMappings.Values)
            {
                if (entityType.IsAssignableFrom(mapping.EntityType))
                {
                    tableMapping = mapping;
                    break;
                }
            }
            if (tableMapping != null)
            {
                FieldMapping fieldMapping = null;
                foreach (DictionaryEntry entry in tableMapping.FieldMappings)
                {
                    fieldMapping = entry.Value as FieldMapping;
                    if (fieldMapping != null && fieldMapping.FieldName == fieldName)
                    {
                        return fieldMapping.PropertyName;
                    }
                }
            }
            return null;
        }
        public string GetTableName(Type entityType)
        {
            foreach (TableMapping mapping in tableMappings.Values)
            {
                if (entityType.IsAssignableFrom(mapping.EntityType))
                {
                    return mapping.TableName;
                }
            }
            return null;
        }

        #endregion

        #region Private Methods

        private void Initialize(XmlNode configSection)
        {
            if (configSection == null)
            {
                return;
            }
            XmlAttributeCollection attributeCollection = configSection.Attributes;

            XmlAttribute att = attributeCollection["defaultLanguage"];
            if (att != null)
            {
                defaultLanguage = att.Value;
            }
            else
            {
                defaultLanguage = "zh-CN";
            }
            att = attributeCollection["userDefaultAvatarPath"];
            if (att != null)
            {
                userDefaultAvatarPath = att.Value;
            }
            att = attributeCollection["orMappingFile"];
            if (att != null)
            {
                orMappingFile = att.Value;
            }
            else
            {
                orMappingFile = @"ORMappings.xml";
            }
            att = attributeCollection["siteUrlsFile"];
            if (att != null)
            {
                urlsFile = att.Value;
            }
            else
            {
                urlsFile = @"Urls.xml";
            }
            att = attributeCollection["userType"];
            if (att != null)
            {
                userType = Type.GetType(att.Value);
            }
            if (userType == null || !typeof(User).IsAssignableFrom(userType))
            {
                throw new Exception("UserType incorrect.");
            }

            XmlAttribute roles = attributeCollection["registeredDefaultRoles"];
            if (roles != null)
            {
                registeredDefaultRoles = roles.Value.Split(';');
            }
            roles = attributeCollection["anonymousDefaultRoles"];
            if (roles != null)
            {
                anonymousDefaultRoles = roles.Value.Split(';');
            }

            try
            {
                if (attributeCollection["userOnlineTimeWindow"] != null &&
                    !string.IsNullOrEmpty(attributeCollection["userOnlineTimeWindow"].Value))
                {
                    userOnlineTimeWindow = int.Parse(attributeCollection["userOnlineTimeWindow"].Value);
                }
            }
            catch
            { }

            try
            {
                if (attributeCollection["administratorUserId"] != null &&
                    !string.IsNullOrEmpty(attributeCollection["administratorUserId"].Value))
                {
                    administratorUserId = int.Parse(attributeCollection["administratorUserId"].Value);
                }
            }
            catch
            { }

            GetProviders(configSection);
            GetJobs(configSection);
            GetSites(configSection);

            string file = Globals.MapPath(ORMappingFile);
            XmlDocument doc = new XmlDocument();
            doc.Load(file);
            GetTableMappings(doc.DocumentElement);
            GetCommands(doc.DocumentElement);
            GetEntityRequests(doc.DocumentElement);
        }
        private void GetProviders(XmlNode configSection)
        {
            providers = new Hashtable();
            foreach (XmlNode child in configSection.ChildNodes)
            {
                if (child.Name == "providers")
                {
                    foreach (XmlNode provider in child.ChildNodes)
                    {
                        switch (provider.Name)
                        {
                            case "add":
                                providers.Add(provider.Attributes["name"].Value, ProviderFactory.Instance.CreateProvider(provider.Attributes["name"].Value, new ProviderData(provider.Attributes)));
                                break;
                            case "remove":
                                providers.Remove(provider.Attributes["name"].Value);
                                break;
                            case "clear":
                                providers.Clear();
                                break;
                        }
                    }
                    break;
                }
            }
        }
        private void GetEntityRequests(XmlNode configSection)
        {
            entityRequests = new Hashtable();
            foreach (XmlNode requestNode in configSection.SelectSingleNode(@"entityRequests").ChildNodes)
            {
                ICondition rootCondition = CreateCondition(requestNode.FirstChild.FirstChild);
                ProcessConditionNode(requestNode.FirstChild.FirstChild, rootCondition);
                entityRequests[Type.GetType(requestNode.Attributes["requestType"].Value)] = rootCondition;
            }
        }
        private ICondition CreateCondition(XmlNode currentNode)
        {
            if (currentNode.Name != "and" && currentNode.Name != "or" && currentNode.Name != "fieldCondition")
            {
                throw new Exception("Invalid condition node name.");
            }
            else if (currentNode.Name == "and")
            {
                return new AndCondition();
            }
            else if (currentNode.Name == "or")
            {
                return new OrCondition();
            }
            else if (currentNode.Name == "fieldCondition")
            {
                return new FieldCondition();
            }
            return null;
        }
        private void ProcessConditionNode(XmlNode currentNode, ICondition currentCondition)
        {
            if (currentNode.Name == "and")
            {
                foreach (XmlNode childNode in currentNode.ChildNodes)
                {
                    ICondition childCondition = CreateCondition(childNode);
                    ProcessConditionNode(childNode, childCondition);
                    currentCondition.ChildConditions.Add(childCondition);
                }
            }
            else if (currentNode.Name == "or")
            {
                foreach (XmlNode childNode in currentNode.ChildNodes)
                {
                    ICondition childCondition = CreateCondition(childNode);
                    ProcessConditionNode(childNode, childCondition);
                    currentCondition.ChildConditions.Add(childCondition);
                }
            }
            else if (currentNode.Name == "fieldCondition")
            {
                ProcessFieldCondition(currentNode, (FieldCondition)currentCondition);
            }
        }
        private void ProcessFieldCondition(XmlNode fieldConditionNode, FieldCondition fieldCondition)
        {
            //Process propertyName
            XmlAttribute attribute = fieldConditionNode.Attributes["propertyName"];
            if (attribute != null && !string.IsNullOrEmpty(attribute.Value))
            {
                fieldCondition.PropertyName = attribute.Value;
            }

            //Process dbFieldValue
            attribute = fieldConditionNode.Attributes["dbFieldValue"];
            if (attribute != null && !string.IsNullOrEmpty(attribute.Value))
            {
                fieldCondition.DbFieldValue = attribute.Value;
            }

            //Process dataValidatorType
            attribute = fieldConditionNode.Attributes["dataValidatorType"];
            if (attribute != null && !string.IsNullOrEmpty(attribute.Value))
            {
                fieldCondition.DataValidatorType = Type.GetType(attribute.Value);
            }

            //Process dbFieldName
            attribute = fieldConditionNode.Attributes["dbFieldName"];
            if (attribute != null && !string.IsNullOrEmpty(attribute.Value))
            {
                fieldCondition.DbFieldName = attribute.Value;
            }
            else
            {
                fieldCondition.DbFieldName = fieldCondition.PropertyName;
            }

            //Process dbOperator
            attribute = fieldConditionNode.Attributes["dbOperator"];
            if (attribute != null && !string.IsNullOrEmpty(attribute.Value))
            {
                fieldCondition.DbOperator = attribute.Value;
            }
            else
            {
                fieldCondition.DbOperator = "=";
            }

            //Process parameterName
            attribute = fieldConditionNode.Attributes["parameterName"];
            if (attribute != null && !string.IsNullOrEmpty(attribute.Value))
            {
                fieldCondition.ParameterName = attribute.Value;
            }
            else
            {
                fieldCondition.ParameterName = "@" + fieldCondition.PropertyName;
            }

            //Process child condition
            if (fieldConditionNode.FirstChild != null)
            {
                IChildCondition rootChildCondition = CreateChildCondition(fieldConditionNode.FirstChild);
                ProcessChildConditionNode(fieldConditionNode.FirstChild, rootChildCondition);
                fieldCondition.ChildCondition = rootChildCondition;
            }
        }

        private IChildCondition CreateChildCondition(XmlNode currentNode)
        {
            if (currentNode.Name != "and" && currentNode.Name != "or" && currentNode.Name != "propertyCondition")
            {
                throw new Exception("Invalid child condition node name.");
            }
            else if (currentNode.Name == "and")
            {
                return new AndChildCondition();
            }
            else if (currentNode.Name == "or")
            {
                return new OrChildCondition();
            }
            else if (currentNode.Name == "propertyCondition")
            {
                return new PropertyCondition();
            }
            return null;
        }
        private void ProcessChildConditionNode(XmlNode currentNode, IChildCondition currentChildCondition)
        {
            if (currentNode.Name == "and")
            {
                foreach (XmlNode childNode in currentNode.ChildNodes)
                {
                    IChildCondition childCondition = CreateChildCondition(childNode);
                    ProcessChildConditionNode(childNode, childCondition);
                    currentChildCondition.ChildConditions.Add(childCondition);
                }
            }
            else if (currentNode.Name == "or")
            {
                foreach (XmlNode childNode in currentNode.ChildNodes)
                {
                    IChildCondition childCondition = CreateChildCondition(childNode);
                    ProcessChildConditionNode(childNode, childCondition);
                    currentChildCondition.ChildConditions.Add(childCondition);
                }
            }
            else if (currentNode.Name == "propertyCondition")
            {
                ProcessPropertyCondition(currentNode, (PropertyCondition)currentChildCondition);
            }
        }
        private void ProcessPropertyCondition(XmlNode propertyConditionNode, PropertyCondition propertyCondition)
        {
            XmlAttribute attribute = propertyConditionNode.Attributes["propertyName"];
            if (attribute != null && !string.IsNullOrEmpty(attribute.Value))
            {
                propertyCondition.PropertyName = attribute.Value;
            }
            attribute = propertyConditionNode.Attributes["propertyValue"];
            if (attribute != null && !string.IsNullOrEmpty(attribute.Value))
            {
                propertyCondition.PropertyValue = attribute.Value;
            }
            attribute = propertyConditionNode.Attributes["dataValidatorType"];
            if (attribute != null && !string.IsNullOrEmpty(attribute.Value))
            {
                propertyCondition.DataValidatorType = Type.GetType(attribute.Value);
            }
        }

        private void GetCommands(XmlNode configSection)
        {
            commands = new Hashtable();
            foreach (XmlNode commandNode in configSection.SelectSingleNode(@"commands").ChildNodes)
            {
                commands[commandNode.Attributes["commandId"].Value] = new Command(commandNode);
            }
        }
        private void GetTableMappings(XmlNode configSection)
        {
            tableMappings = new Hashtable();
            foreach (XmlNode tableMappingNode in configSection.SelectSingleNode(@"tableMappings").ChildNodes)
            {
                tableMappings[Type.GetType(tableMappingNode.Attributes["entityType"].Value)] = new TableMapping(tableMappingNode);
            }
        }
        private void GetJobs(XmlNode configSection)
        {
            jobs = new Hashtable();
            foreach (XmlNode child in configSection.ChildNodes)
            {
                if (child.Name == "jobs")
                {
                    foreach (XmlNode jobNode in child.ChildNodes)
                    {
                        if (jobNode.NodeType != XmlNodeType.Comment)
                        {
                            XmlAttribute typeAttribute = jobNode.Attributes["type"];
                            XmlAttribute nameAttribute = jobNode.Attributes["name"];

                            Type type = Type.GetType(typeAttribute.Value);
                            if (type != null)
                            {
                                if (!jobs.Contains(nameAttribute.Value))
                                {
                                    Job job = new Job(type, jobNode);
                                    jobs[nameAttribute.Value] = job;
                                }
                            }
                        }
                    }
                    break;
                }
            }
        }
        private void GetSites(XmlNode configSection)
        {
            sites = new Hashtable();
            foreach (XmlNode child in configSection.ChildNodes)
            {
                if (child.Name == "sites")
                {
                    foreach (XmlNode siteNode in child.ChildNodes)
                    {
                        if (siteNode.NodeType != XmlNodeType.Comment)
                        {
                            XmlAttribute nameAttribute = siteNode.Attributes["name"];
                            XmlAttribute typeAttribute = siteNode.Attributes["type"];
                            XmlAttribute entityProviderNameAttribute = siteNode.Attributes["entityProviderName"];

                            Type type = Type.GetType(typeAttribute.Value);
                            if (type != null)
                            {
                                if (!sites.Contains(nameAttribute.Value))
                                {
                                    ISite site = Activator.CreateInstance(type) as ISite;
                                    if (site == null)
                                    {
                                        throw new Exception(string.Format("The site {0} has the incorrect type {1}.", nameAttribute.Value, typeAttribute.Value));
                                    }
                                    site.EntityProvider = providers[entityProviderNameAttribute.Value] as EntityProvider;
                                    sites[nameAttribute.Value] = site;
                                }
                            }
                            else
                            {
                                throw new Exception(string.Format("The site {0} has the incorrect type {1}.", nameAttribute.Value, typeAttribute.Value));
                            }
                        }
                    }
                    break;
                }
            }
        }

        #endregion
    }
}